
#include "board.h"
#include <rtthread.h>
#include "small_modbus.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <rtdevice.h>
#include "board_virtualIO.h"

#define MB_SLAVE_ADDR (1U)

/* CH9121串口设备资源 */
#define CLIENT_SAMPLE_UART_NAME  "uart3"
#define CLIENT_CONFIG_MODE_PIN   GET_PIN(D, 10)
#define CLIENT_RESET_DEFAULT_PIN GET_PIN(D, 11)

/* 串口设备句柄 */
static rt_device_t serial;
/* 初始化配置参数 */
struct serial_configure configx = RT_SERIAL_CONFIG_DEFAULT;
static rt_sem_t rx_sem;
static rt_thread_t rx_recv_thread;

struct serial_recv_ret
{
    rt_int8_t rev_buff[128];
    rt_uint8_t rev_len;
    rt_bool_t rev_over;
};

struct serial_recv_ret sprt;
static rt_mailbox_t mb_handle;

#if 0
static void config_mode_init(void)
{
    rt_pin_mode(CLIENT_RESET_DEFAULT_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(CLIENT_RESET_DEFAULT_PIN, PIN_HIGH);
    rt_pin_mode(CLIENT_CONFIG_MODE_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(CLIENT_CONFIG_MODE_PIN, PIN_HIGH);
}

static void config_mode_write(rt_uint8_t cmd, rt_uint8_t *params, rt_uint8_t params_len)
{
    rt_pin_write(CLIENT_CONFIG_MODE_PIN, PIN_LOW);
    rt_uint8_t phead[3] = {0x57, 0xab, 0x00};
    phead[3] = cmd;
    rt_device_write(serial, 0, phead, 3);
    if (params_len != 0xff)
    {
        rt_device_write(serial, 0, params, params_len);
    }
    rt_pin_write(CLIENT_CONFIG_MODE_PIN, PIN_HIGH);
}
#endif

static rt_err_t rx_callback(rt_adc_device_t dev, rt_size_t size)
{
    rt_sem_release(rx_sem);
    return RT_EOK;
}

static void rx_thread_entry(void* parameter)
{
    while (1)
    {
        rt_sem_take(rx_sem, RT_WAITING_FOREVER);
        sprt.rev_len = rt_device_read(serial, 0, sprt.rev_buff, 128);

#if (SMALL_MODBUS_USING_MSG_DEBUG)
        uint8_t idx;
        rt_kprintf("sp rlen:%d, data:\n", sprt.rev_len);
        for (idx = 0; idx < sprt.rev_len; idx++)
        {
            rt_kprintf("%x ", sprt.rev_buff[idx] & 0x0000ff);
        }
        rt_kprintf("\n");
#endif
        // if (sprt.rev_len == 12)
        rt_mb_send(mb_handle, (rt_uint32_t)&sprt.rev_buff);
    }
}

static int ch9121_serial_port_server(void)
{
    rt_uint32_t slen = 0;

    /* 查找串口设备 */
    serial = rt_device_find(CLIENT_SAMPLE_UART_NAME);
    if (serial == NULL)
    {
        rt_kprintf("can not find serial device:%s\r\n", CLIENT_SAMPLE_UART_NAME);
        return RT_ERROR;
    }

    rt_kprintf("ch9121 device:%s found.\r\n", CLIENT_SAMPLE_UART_NAME);

    /* step2：修改串口配置参数 */
    configx.baud_rate = BAUD_RATE_115200;  // 波特率 115200
    configx.data_bits = DATA_BITS_8;       // 数据位 8
    configx.stop_bits = STOP_BITS_1;       // 停止位 1
    configx.bufsz = 512;                   // 修改缓冲区 512
    configx.parity = PARITY_NONE;          // 无奇偶校验位

    /* step3：控制串口设备。通过控制接口传入命令控制字，与控制参数 */
    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &configx);

    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_DMA_RX);

    /* 设置接收回调函数  */
    rt_device_set_rx_indicate(serial, rx_callback);
    /* 创建信号量  */
    rx_sem = rt_sem_create("rx_sem", 0, RT_IPC_FLAG_PRIO);
    /* 创建串口接收线程  */
    rx_recv_thread = rt_thread_create("rx_recv", rx_thread_entry, 0, 1024, 20, 5);
    rt_thread_startup(rx_recv_thread);

    return RT_EOK;
}

rt_size_t serial_server_write(unsigned char* strx, int len)
{
#if (SMALL_MODBUS_USING_MSG_DEBUG)
    rt_uint8_t idx;
    rt_kprintf("rsp:");
    for (idx = 0; idx < len; idx++)
    {
        rt_kprintf("%x ", strx[idx] & 0x0000ff);
    }
    rt_kprintf("\n");
#endif
    return rt_device_write(serial, 0, strx, len);
}

/*
    从机回调函数, 当从机接收到主机的请求(数据校验和地址功能码已经解析完)
    在这个回调函数内填充数据，返回数据的长度即可
*/
static int modbus_tcp_slave_callback(small_modbus_t* smb, int function_code, int addr, int num, void* read_write_data)
{
    int rc = 0;
    switch (function_code)
    {
        case MODBUS_FC_READ_HOLDING_REGISTERS:
        {
            if ((0 <= addr) && (addr < HOLD_REG_MAX))
            {
                rc = vio_read_hold_regs(addr, num, read_write_data);
            }
        }
        break;
        case MODBUS_FC_READ_INPUT_REGISTERS:
            break;
        case MODBUS_FC_WRITE_SINGLE_REGISTER:
        case MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
        {
            if ((0 <= addr) && (addr < HOLD_REG_MAX))
            {
                rc = vio_write_hold_regs(addr, num, read_write_data);
            }
        }
        break;
    }
    return rc;
}

static small_modbus_t modbus_tcp_slave = {0};

static void mbtcp_slave_on_serial_thread_entry(void* param)
{
    int rc = 0;
    rt_int8_t* mbox_recv;
    small_modbus_t* smb_slave = param;

    // modbus从站初始化
    modbus_init(smb_slave, MODBUS_CORE_TCP, modbus_port_rtsocket_create(MODBUS_DEVICE_SLAVE, "127.0.0.1", "502"));
    // 设置从站地址 0x01
    modbus_set_slave(smb_slave, MB_SLAVE_ADDR);
    while (1)
    {
        if (rt_mb_recv(mb_handle, (rt_uint32_t*)&mbox_recv, RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_memcpy(smb_slave->read_buff, mbox_recv, sprt.rev_len);
#if (SMALL_MODBUS_USING_MSG_DEBUG)
            uint8_t idx;
            rt_kprintf("mail rcv:");
            for (idx = 0; idx < sprt.rev_len; idx++)
            {
                rt_kprintf("%x ", mbox_recv[idx] & 0x0000ff);
            }
            rt_kprintf("\n");
#endif
            rc = modbus_slave_wait_handle(smb_slave, modbus_tcp_slave_callback, MODBUS_WAIT_FOREVER, sprt.rev_len);
            if (rc <= 0)
            {
                modbus_error_recovery(smb_slave);
            }
        }
    }
}

// 创建modbus_tcp_on_serial_thread线程
int bms_mbtcp_slave_on_serial_thread(void)
{
    rt_thread_t tid;

    mb_handle = rt_mb_create("mbt", 32, RT_IPC_FLAG_FIFO);

    if (RT_EOK != ch9121_serial_port_server())
    {
        return RT_ERROR;
    }

    tid = rt_thread_create("mtss", mbtcp_slave_on_serial_thread_entry, &modbus_tcp_slave, B_MB_CHT_COM_STACK_SIZE, 10, 20);
    if (tid != RT_NULL)
    {
        rt_thread_startup(tid);
    }
    else
    {
        goto __exit;
    }

    return RT_EOK;

__exit:
    if (tid)
        rt_thread_delete(tid);
    return RT_ERROR;
}
